package org.beaconframework.vaadin.data;

import java.io.IOException;
import java.util.*;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.beaconframework.util.exception.BeaconException;
import org.beaconframework.util.xml.DomSource;
import org.beaconframework.util.xml.DomUtil;
import org.beaconframework.util.xml.XPathResolverRegistry;
import org.beaconframework.vaadin.util.VaadinContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.util.IndexedContainer;

public class XmlContainerBuilder {

	Logger log = LoggerFactory.getLogger(XmlContainerBuilder.class);
	private XPathResolverRegistry resolverRegistry = new XPathResolverRegistry();
	List<String> propertyList = new ArrayList<String>();
	Map<String, String> xpathMap = new HashMap<String, String>();
	String rowXPath = null;

	DomSource domSource;
	
	


	public XPathResolverRegistry getXPathResolverRegistry() {
		return resolverRegistry;

	}

	private XPath newXPath() {
		XPath xpath = XPathFactory.newInstance().newXPath();
		getXPathResolverRegistry().attach(xpath);
		return xpath;
	}

	public void setRowXPath(String xpath) {
		rowXPath = xpath;
	}

	public String getRowXPath() {
		return rowXPath;
	}

	public void addXPathProperty(String pname, String xpath) {
		propertyList.add(pname);
		xpathMap.put(pname, xpath);
	}

	public XPathResolverRegistry getResolverRegistry() {
		return resolverRegistry;
	}

	public void setResolverRegistry(XPathResolverRegistry resolverRegistry) {
		this.resolverRegistry = resolverRegistry;
	}

	public IndexedContainer createIndexedContainer() {
	
			return createIndexedContainer((Node)getXmlSource().getNode());
		
	}
	
	public IndexedContainer updateIndexedContainer(IndexedContainer ic) {
		
			return updateIndexedContainer((Node)getXmlSource().getNode(),ic);
		
	
			
		
	}
	public IndexedContainer createIndexedContainer(Node d) {
		IndexedContainer ic = new IndexedContainer();
		updateIndexedContainer(d, ic);
		return ic;
	}

	private Node doTransform(Node input) {
		return input;
	}

	public IndexedContainer updateIndexedContainer(Node input, IndexedContainer ic) {
		for (String pname : propertyList) {
			ic.addContainerProperty(pname, String.class, "");
		}

		ic.removeAllItems();
		try {

			Node transformedDocument = doTransform(input);

			XPath xpath = newXPath();
			String rx = getRowXPath();
			getResolverRegistry().attach(xpath);

			if (rx == null) {
				throw new NullPointerException(
						"XPathExpressionContainer.rowXPath cannot be null");
			}
			XPathExpression x = xpath.compile(getRowXPath());

			addDocument(transformedDocument, x, ic);
			return ic;
		} catch (XPathExpressionException e) {
			throw new BeaconException(e);
		}
	}

	private void addDocument(Node d, XPathExpression x, IndexedContainer ic)
			throws XPathExpressionException {
		
		NodeList nl = (NodeList) x.evaluate(d, XPathConstants.NODESET);
		
		for (int i = 0; nl != null && i < nl.getLength(); i++) {

			Node n = nl.item(i);
			if (log.isTraceEnabled()) {
				log.trace("adding node #{}: {}", i + 1, n);

				DomUtil.logTrace(log, "node #" + (i + 1), n);
			}
			addItem(n, ic);
		}
	}
	
	private void addItem(Node n, IndexedContainer ic) {
		try {
		Object id = ic.addItem();
		Item item = ic.getItem(id);
		
		XPath x = newXPath();
		for (String pname: propertyList) {
			String xp = xpathMap.get(pname);
			String val = x.evaluate(xp, n);
			Property prop = item.getItemProperty(pname);
			if (prop!=null) {
				prop.setValue(val);
			}
		}
		}
		catch (XPathExpressionException xpe) {
			throw new BeaconException(xpe);
		}
	}
	
	
	public void setXmlSource(DomSource n) {
		domSource = n;
	}
	public DomSource getXmlSource() {
		
		return domSource;
	}
	
	
	
	

}
